//
//  Label.swift
//  live
//
//  Created by melvyn on 2018/8/16.
//  Copyright © 2018 NEET. All rights reserved.
//

import UIKit

extension UILabel {
    private struct AssociatedKeys {
        
        static var top = "UILabel_Additions_top_associatedKey"
        static var left = "UILabel_Additions_left_associatedKey"
        static var bottom = "UILabel_Additions_bottom_associatedKey"
        static var right = "UILabel_Additions_right_associatedKey"
        static var insets = "UILabel_Additions_insets_associatedKey"
    }
    
    @IBInspectable var topInset: CGFloat {
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.top, newValue, .OBJC_ASSOCIATION_ASSIGN)
            let textEdgeInsets = self.textEdgeInsets
            self.textEdgeInsets = UIEdgeInsets(top: newValue, left: textEdgeInsets.left, bottom: textEdgeInsets.bottom, right: textEdgeInsets.right)
        }
        get { return objc_getAssociatedObject(self, &AssociatedKeys.top) as? CGFloat ?? 0 }
    }
    
    @IBInspectable var leftInset: CGFloat {
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.left, newValue, .OBJC_ASSOCIATION_ASSIGN)
            let textEdgeInsets = self.textEdgeInsets
            self.textEdgeInsets = UIEdgeInsets(top: textEdgeInsets.top, left: newValue, bottom: textEdgeInsets.bottom, right: textEdgeInsets.right)
        }
        get { return objc_getAssociatedObject(self, &AssociatedKeys.left) as? CGFloat ?? 0 }
    }
    
    @IBInspectable var bottomInset: CGFloat {
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.bottom, newValue, .OBJC_ASSOCIATION_ASSIGN)
            let textEdgeInsets = self.textEdgeInsets
            self.textEdgeInsets = UIEdgeInsets(top: textEdgeInsets.top, left: textEdgeInsets.left, bottom: newValue, right: textEdgeInsets.right)
        }
        get { return objc_getAssociatedObject(self, &AssociatedKeys.bottom) as? CGFloat ?? 0 }
    }
    
    @IBInspectable var rightInset: CGFloat {
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.right, newValue, .OBJC_ASSOCIATION_ASSIGN)
            let textEdgeInsets = self.textEdgeInsets
            self.textEdgeInsets = UIEdgeInsets(top: textEdgeInsets.top, left: textEdgeInsets.left, bottom: textEdgeInsets.bottom, right: newValue)
        }
        get { return objc_getAssociatedObject(self, &AssociatedKeys.right) as? CGFloat ?? 0 }
    }
    
    var textEdgeInsets: UIEdgeInsets {
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.insets, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            invalidateIntrinsicContentSize()
        }
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.insets) as? UIEdgeInsets ?? .zero
        }
    }
    
    @objc func _textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
        
        #if swift(>=4.2)
        let insetRect = bounds.inset(by: textEdgeInsets)
        #else
        let insetRect = UIEdgeInsetsInsetRect(bounds, textEdgeInsets)
        #endif
        
        let textRect = self._textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
        let invertedInsets = UIEdgeInsets(top: -textEdgeInsets.top,
                                          left: -textEdgeInsets.left,
                                          bottom: -textEdgeInsets.bottom,
                                          right: -textEdgeInsets.right)
        #if swift(>=4.2)
        return textRect.inset(by: invertedInsets)
        #else
        return UIEdgeInsetsInsetRect(textRect, invertedInsets)
        #endif
    }
    
    @objc func _drawText(in rect: CGRect) {
        #if swift(>=4.2)
        self._drawText(in: rect.inset(by: textEdgeInsets))
        #else
        self._drawText(in: UIEdgeInsetsInsetRect(rect, textEdgeInsets))
        #endif
    }
}

extension UILabel: MethodSwizzling {
    
    static func swizzling() {
        swizzle(self,
                selector: #selector(textRect(forBounds:limitedToNumberOfLines:)),
                with: #selector(_textRect(forBounds:limitedToNumberOfLines:)))
        swizzle(self,
                selector: #selector(drawText(in:)), with: #selector(_drawText(in:)))
    }
}

